﻿using System;
using System.Collections.Generic;
using DebIt.Business;
using DebIt.Common.Model;

namespace DebIt.Service
{
    public class DebitService : IDisposable
    {
        #region variables

        private bool _isSusppended;
        private static string _connectionString;
        private DebitBusinessComponent _businessComponent;

        private readonly Dictionary<int, Client> _clients = new Dictionary<int, Client>();
        private readonly Dictionary<int, Loan> _loans = new Dictionary<int, Loan>();

        #endregion


        #region Events

        public event Action<object, Client> NewClientAddedEvent;
        public event Action<object, Client> ClientDeletedEvent;
        public event Action<object, Loan> NewLoanAddedEvent;
        public event Action<object, Loan> LoanDeletedEvent;

        #endregion


        #region constructors

        public DebitService(string connectionString)
        {
            _connectionString = connectionString;
            _businessComponent = new DebitBusinessComponent(_connectionString);
            Load();
        }

        ~DebitService()
        {
            Dispose(false);
        }

        #endregion


        #region methods

        #region Clients

        public Client NewClient()
        {
            var client = new Client();
            client.ClientId = _businessComponent.Add(client.ConvertToPo());
            _clients.Add(client.ClientId, client);
            if (NewClientAddedEvent != null)
                NewClientAddedEvent(this, client);
            return client;
        }

        public Client GetClient(int id)
        {
            Client client;
            return _clients.TryGetValue(id, out client) ? client : null;
        }

        public List<Client> GetAllClients()
        {
            return new List<Client>(_clients.Values);
        }

        public void Update(Client client)
        {
            _businessComponent.Update(client.ConvertToPo());
        }

        public void Delete(Client client)
        {
            _businessComponent.DeleteClient(client.ClientId);
            _clients.Remove(client.ClientId);
            foreach (var cls in client.ClientLoanStates)
                cls.Loan.ClientLoanStates.Remove(cls);
            if (ClientDeletedEvent != null)
                ClientDeletedEvent(this, client);
        }

        #endregion


        #region Loan

        public Loan NewLoan()
        {
            var loan = new Loan();
            loan.LoanId = _businessComponent.Add(loan.ConvertToPo());
            _loans.Add(loan.LoanId, loan);
            if (NewLoanAddedEvent != null)
                NewLoanAddedEvent(this, loan);
            return loan;
        }

        public Loan GetLoan(int id)
        {
            Loan loan;
            return _loans.TryGetValue(id, out loan) ? loan : null;
        }

        public List<Loan> GetAllLoans()
        {
            return new List<Loan>(_loans.Values);
        }

        public void Update(Loan loan)
        {
            _businessComponent.Update(loan.ConvertToPo());
        }

        public void Delete(Loan loan)
        {
            _businessComponent.DeleteLoan(loan.LoanId);
            _loans.Remove(loan.LoanId);
            if (LoanDeletedEvent != null)
                LoanDeletedEvent(this, loan);
        }

        #endregion


        #region LoanItem

        public LoanItem NewLoanItemFor(Loan loan)
        {
            var loanItem = new LoanItem { Loan = loan };
            loanItem.LoanItemId = _businessComponent.Add(loanItem.ConvertToPo());
            loan.Add(loanItem);
            return loanItem;
        }

        public void Update(LoanItem loanItem)
        {
            _businessComponent.Update(loanItem.ConvertToPo());
        }

        public void Delete(LoanItem loanItem)
        {
            _businessComponent.DeleteLoanItem(loanItem.LoanItemId);
            loanItem.Loan.LoanItems.Remove(loanItem);
        }

        #endregion


        #region ClientLoanState

        public ClientLoanState NewClientLoanStateFor(Loan loan, Client client)
        {
            var clientLoanState = new ClientLoanState { Loan = loan, Client = client };
            var t = clientLoanState.ConvertToPo();
            clientLoanState.ClientLoanStateId = _businessComponent.Add(clientLoanState.ConvertToPo());
            loan.ClientLoanStates.Add(clientLoanState);
            client.Add(clientLoanState);
            return clientLoanState;
        }

        public void Update(ClientLoanState clientLoanState)
        {
            _businessComponent.Update(clientLoanState.ConvertToPo());
        }

        public void Delete(ClientLoanState clientLoanState)
        {
            _businessComponent.DeleteClientLoanState(clientLoanState.ClientLoanStateId);
            clientLoanState.Client.ClientLoanStates.Remove(clientLoanState);
            clientLoanState.Loan.ClientLoanStates.Remove(clientLoanState);
        }

        #endregion


        #region Loading

        private void Load()
        {
            LoadClients();
            LoadLoans();
            LoadLoanItems();
            LoadClientLoanStates();
        }

        private void LoadClients()
        {
            var clientPos = _businessComponent.GetAllClients();
            clientPos.ForEach(x => _clients.Add(x.ClientId, x.ConvertToModelObject()));
        }

        private void LoadLoans()
        {
            var loanPos = _businessComponent.GetAllLoans();
            loanPos.ForEach(x => _loans.Add(x.LoanId, x.ConvertToModelObject()));
        }

        private void LoadLoanItems()
        {
            var loanItems = _businessComponent.GetAllLoanItems();
            loanItems.ForEach(x =>
            {
                if (_loans.ContainsKey(x.LoanId))
                {
                    var loanItem = x.ConvertToModelObject();
                    _loans[x.LoanId].Add(loanItem);
                    loanItem.Loan = _loans[x.LoanId];
                }
            });
        }

        private void LoadClientLoanStates()
        {
            var loanClientStates = _businessComponent.GetAllLoanClientStates();
            loanClientStates.ForEach(x =>
                                         {
                                             if (_clients.ContainsKey(x.ClientId) && _loans.ContainsKey(x.LoanId))
                                             {
                                                 var loanClientState = x.ConvertToModelObject();
                                                 _loans[x.LoanId].ClientLoanStates.Add(loanClientState);
                                                 loanClientState.Loan = _loans[x.LoanId];
                                                 _clients[x.ClientId].Add(loanClientState);
                                                 loanClientState.Client = _clients[x.ClientId];
                                             }
                                         });
        }

        public void ReLoad()
        {
            _clients.Clear();
            _loans.Clear();
            Load();
        }

        #endregion

        public void Suspend()
        {
            if (_isSusppended)
                return;
            _businessComponent.Dispose();
            _businessComponent = null;
            _isSusppended = true;
        }

        public void WakeUp()
        {
            if (_isSusppended)
                _businessComponent = new DebitBusinessComponent(_connectionString);
            else
                ReLoad();
            _isSusppended = false;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            _businessComponent.Dispose();
            _businessComponent = null;
        }

        #endregion
    }
}
